<!--
@license
Copyright 2016 The TensorFlow Authors. All Rights Reserved.

Licensed under the Apache License, Version 2.0 (the "License");
you may not use this file except in compliance with the License.
You may obtain a copy of the License at

    http://www.apache.org/licenses/LICENSE-2.0

Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->

<link rel="import" href="../polymer/polymer.html">
<link rel="import" href="../tf-dashboard-common/tensorboard-color.html">

<dom-module id="tf-graph-icon">
  <style>
    .faded-rect {
      fill: url(#rectHatch);
    }

    .faded-ellipse {
      fill: url(#ellipseHatch);
    }

    .faded-rect, .faded-ellipse, .faded-series {
      stroke:   var(--tb-graph-faded) !important;
    }
  </style>
  <template>
    <template is="dom-if" if="[[_isType(node, type, 'OP')]]">
      <template is="dom-if" if="[[_isConst(node, const)]]">
        <svg height$="[[height]]"
            preserveAspectRatio="xMinYMid meet" viewBox="0 0 10 10">
          <circle cx="5" cy="5" r="3"
              fill$="[[_getFill(_computedFill, 'OP')]]"
              stroke$="[[_getStroke(_computedFill, 'OP')]]" />
        </svg>
      </template>
      <template is="dom-if" if="[[_isSummary(node, summary)]]">
        <svg width$="[[height]]" height$="[[height]]" viewBox="0 0 12 12">
          <use x="0" y="0" xlink:href="#summary-icon" />
        </svg>
      </template>
      <template is="dom-if" if="[[_isRegularOp(node, const, summary)]]">
        <svg height$="[[height]]"
            preserveAspectRatio="xMinYMid meet" viewBox="0 0 16 8">
          <use xmlns:xlink="http://www.w3.org/1999/xlink"
              xlink:href="#op-node-stamp"
              fill$="[[_getFill(_computedFill, 'OP')]]"
              stroke$="[[_getStroke(_computedFill, 'OP')]]"
              class$="{{_fadedClass(renderInfo, 'ellipse')}}"
              x="8" y="4" />
        </svg>
      </template>
    </template>
    <template is="dom-if" if="[[_isType(node, type, 'META')]]">
      <svg height$="[[height]]"
            preserveAspectRatio="xMinYMid meet" viewBox="0 0 37 16">
        <rect x="1" y="1"
            fill$="[[_getFill(_computedFill, 'META')]]"
            stroke$="[[_getStroke(_computedFill, 'META')]]"
            class$="{{_fadedClass(renderInfo, 'rect')}}"
            stroke-width="2px"
            height="14" width="35"
            rx="5" ry="5"/>
      </svg>
    </template>
    <template is="dom-if" if="[[_isType(node, type, 'SERIES')]]">
      <template is="dom-if" if="[[_isVertical(node, vertical)]]">
        <svg height$="[[height]]"
            preserveAspectRatio="xMinYMid meet" viewBox="0 0 16 15">
          <use xmlns:xlink="http://www.w3.org/1999/xlink"
              xlink:href="#op-series-vertical-stamp"
              fill$="[[_getFill(_computedFill, 'SERIES')]]"
              stroke$="[[_getStroke(_computedFill, 'SERIES')]]"
              class$="{{_fadedClass(renderInfo, 'series')}}"
              x="0" y="2" />
        </svg>
      </template>
      <template is="dom-if" if="[[!_isVertical(node, vertical)]]">
        <svg height$="[[height]]"
            preserveAspectRatio="xMinYMid meet" viewBox="0 0 24 10">
          <use xmlns:xlink="http://www.w3.org/1999/xlink"
              xlink:href="#op-series-horizontal-stamp"
              fill$="[[_getFill(_computedFill, 'SERIES')]]"
              stroke$="[[_getStroke(_computedFill, 'SERIES')]]"
              class$="{{_fadedClass(renderInfo, 'series')}}"
              x="0" y="1" />
        </svg>
      </template>
    </template>
  </template>

  <script>
    (function() {
      Polymer({
        is: 'tf-graph-icon',

        properties: {
          /**
           * Node to represent with an icon. Optional, but if specified, its
           * properties override those defined in the type, vertical, const and
           * summary properties.
           * @type {tf.graph.Node}
           */
          node: {
            type: Object,
            value: null
          },

          /**
           * Render node information associated with this node. Optional. If
           * specified, this is only used when computing the fill of the icon
           * element.
           * @type {tf.graph.render.RenderNodeInfo}
           */
          renderInfo: {
            type: Object,
            value: null
          },

          /**
           * String indicating the type of coloring to use for this node, used
           * only for determining the fill.
           */
          colorBy: {
            type: Object,
            value: "structural"
          },

          /**
           * Function used by structural coloring algorithm to determine which
           * color to use based on the template ID of the node. Optional.
           */
          templateIndex: {
            type: Function,
            value: null
          },

          /** Type of node to draw (ignored if node is set). */
          type: {
            type: String,
            value: null
          },

          /** Direction for series (ignored for other types). */
          vertical: {
            type: Boolean,
            value: false
          },

          /** Whether the op is Const (ignored for non-ops). */
          const: {
            type: Boolean,
            value: false
          },

          /** Whether the op is a Summary (ignored for non-ops). */
          summary: {
            type: Boolean,
            value: false
          },

          /**
           * Fill for the icon, optional. If fill is specified and node is not
           * specified, then this value will override the default for the
           * element. However, if node is specified, this value will be ignored.
           */
          fill: {
            type: String,
            value: null
          },

          /** Height of the SVG element in pixels, used for scaling. */
          height: {
            type: Number,
            value: 20
          },

          /** The computed fill for the node. **/
          _computedFill: {
            type: String,
            computed:
              "_getComputedFill(node, renderInfo, colorBy, templateIndex, fill)"
          }

        },

        /**
         * Get the computed fill value for the element.
         */
        _getComputedFill: function(inputNode, inputRenderInfo, inputColorBy,
            inputTemplateIndex, inputFill) {
          if (inputNode && inputRenderInfo &&
              inputColorBy && inputTemplateIndex) {
            var ns = tf.graph.scene.node;
            var colorBy = ns.ColorBy[inputColorBy.toUpperCase()];
            return ns.getFillForNode(inputTemplateIndex, colorBy,
                inputRenderInfo, false);
          }
          return inputFill;
        },

        /**
         * Get the fill value for the element, or if that's not possible, return
         * the default fill value for the node type.
         */
        _getFill: function(inputComputedFill, inputNodeType) {
          return inputComputedFill || ({
            OP: tf.graph.render.OpNodeColors.DEFAULT_FILL,
            META: tf.graph.render.MetanodeColors.DEFAULT_FILL,
            SERIES: tf.graph.render.SeriesNodeColors.DEFAULT_FILL
          })[inputNodeType];
        },

        /**
         * Get the stroke value for the element, or if that's not possible,
         * return the default stroke value for the node type.
         */
        _getStroke: function(inputComputedFill, inputNodeType) {
          return inputComputedFill ?
            tf.graph.scene.node.getStrokeForFill(inputComputedFill) :
            ({
              OP: tf.graph.render.OpNodeColors.DEFAULT_STROKE,
              META: tf.graph.render.MetanodeColors.DEFAULT_STROKE,
              SERIES: tf.graph.render.SeriesNodeColors.DEFAULT_STROKE
            })[inputNodeType];
        },

        /**
         * Test whether the specified node's type, or the literal type string,
         * match a particular other type.
         */
        _isType: function(inputNode, inputType, targetType) {
          if (inputNode) {
            return tf.graph.NodeType[inputNode.type] === targetType;
          }
          return inputType === targetType;
        },

        /**
         * Test whether the specified node should be represented as a vertical
         * series. Defaults to the value of the vertical property if node is
         * not specified.
         */
        _isVertical: function(inputNode, inputVertical) {
          if (inputNode) {
            return inputNode.hasNonControlEdges;
          }
          return !!inputVertical;
        },

        /**
         * Test whether the specified node is a constant. Defaults to the value
         * of the const property if node is not specified.
         */
        _isConst: function(inputNode, inputConst) {
          if (inputNode) {
            return inputNode.op === 'Const';
          }
          return !!inputConst;
        },

        /**
         * Test whether the specified node is a summary. Defaults to the value
         * of the summary property if node is not specified.
         */
        _isSummary: function(inputNode, inputSummary) {
          if (inputNode) {
            return this._isType(inputNode, null, 'OP') &&
                inputNode.op.substr(-7) === 'Summary';
          }
          return !!inputSummary;
        },

        /**
         * Test whether the op node is a regular non-summary non-const node.
         */
        _isRegularOp: function(inputNode, inputConst, inputSummary) {
          return !this._isConst(inputNode, inputConst) &&
              !this._isSummary(inputNode, inputSummary);
        },

        _fadedClass: function(itemRenderInfo, shape) {
          return itemRenderInfo && itemRenderInfo.isFadedOut ? 'faded-' + shape : '';
        }
      });
    })();
  </script>
</dom-module>
